/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package wechat

import "strings"

/**
接收微信服务器事件推送
在微信用户和公众号产生交互的过程中，用户的某些操作会使得微信服务器通过事件推送
的形式通知到开发者在开发者中心处设置的服务器地址，从而开发者可以获取到该信息。
其中，某些事件推送在发生后，是允许开发者回复用户的，某些则不允许。

微信服务器以 xml 格式 POST 数据到公众号服务器。

参数说明：
 参数	          | 描述
==========================================
 ToUserName	      | 开发者微信号
------------------------------------------
 FromUserName	  | 发送方帐号（一个OpenID）
------------------------------------------
 CreateTime	      | 消息创建时间 （整型）
------------------------------------------
 MsgType	      | 消息类型，event
------------------------------------------
 Event	          | 事件类型
------------------------------------------
 EventKey	      | 事件KEY值
------------------------------------------
 Ticket	          | 二维码的ticket
------------------------------------------
 Latitude	      | 地理位置纬度
------------------------------------------
 Longitude	      | 地理位置经度
------------------------------------------
 Precision	      | 地理位置精度
------------------------------------------

事件类型说明：

名称                                   | Event           |  额外
======================================================================================================
关注                                   | subscribe       |
------------------------------------------------------------------------------------------------------
取消关注                               | unsubscribe     |
------------------------------------------------------------------------------------------------------
未关注用户扫描带参数二维码              | subscribe       |  EventKey：qrscene_为前缀，后面为二维码的参数值
                                                            Ticket：  二维码的ticket，可用来换取二维码图片
------------------------------------------------------------------------------------------------------
已关注用户扫描带参数二维码              | SCAN            |  EventKey: 是一个32位无符号整数，即创建二维码时
                                                                      的二维码scene_id
                                                            Ticket:   二维码的ticket，可用来换取二维码图片
------------------------------------------------------------------------------------------------------
上报地理位置                           | LOCATION        |   Latitude:  地理位置纬度
                                                            Longitude: 地理位置经度
                                                            Precision: 地理位置精度
------------------------------------------------------------------------------------------------------
点击菜单拉取消息                       | CLICK           |   EventKey: 与自定义菜单接口中KEY值对应
------------------------------------------------------------------------------------------------------
点击菜单跳转链接                       | VIEW            |   EventKey: 设置的跳转URL
------------------------------------------------------------------------------------------------------
*/

type Event int

const (
	Subscribe Event = 1 << iota
	Unsubscribe
	Scan
	Location
	Click
	View
)

func StrToEvent(str string) Event {
	str = strings.ToUpper(str)
	switch str {
	case Subscribe.String():
		return Subscribe
	case Unsubscribe.String():
		return Unsubscribe
	case Scan.String():
		return Scan
	case Location.String():
		return Location
	case Click.String():
		return Click
	case View.String():
		return View
	default:
		return 0
	}
}

func (e Event) String() string {
	switch e {
	case Subscribe:
		return "SUBSCRIBE"
	case Unsubscribe:
		return "UNSUBSCRIBE"
	case Scan:
		return "SCAN"
	case Location:
		return "LOCATION"
	case Click:
		return "CLICK"
	case View:
		return "VIEW"
	default:
		return "Undefined"
	}
}

type EventMessage struct {
	ToUserName   string `desc:"开发者微信号"`
	FromUserName string `desc:"发送方帐号（一个OpenID）"`
	CreateTime   int    `desc:"消息创建时间（整型）"`
	MsgType      string `desc:"消息类型，event"`
	Event        string `desc:"事件类型"`
	EventKey     string `desc:"事件KEY值"`
	Ticket       string `desc:"二维码的ticket，可用来换取二维码图片"`
	Latitude     int    `desc:"地理位置纬度"`
	Longitude    int    `desc:"地理位置经度"`
	Precision    int    `desc:"地理位置精度"`
}

var Dispatcher = NewDispatcher()

type EventHandler func(msg *EventMessage)

type dispatcher struct {
	events map[Event][]EventHandler
}

func (d *dispatcher) Listen(event Event, handler EventHandler) {
	if event & Subscribe!=0 {
		d.events[Subscribe] = append(d.events[Subscribe], handler)
	}
	if event & Unsubscribe!=0 {
		d.events[Unsubscribe] = append(d.events[Unsubscribe], handler)
	}
	if event & Scan!=0 {
		d.events[Scan] = append(d.events[Scan], handler)
	}
	if event & Location!=0 {
		d.events[Location] = append(d.events[Location], handler)
	}
	if event & Click!=0 {
		d.events[Click] = append(d.events[Click], handler)
	}
	if event & View!=0 {
		d.events[View] = append(d.events[View], handler)
	}
}

func (d *dispatcher) Trigger(evt *EventMessage) {
	for _, handler := range d.events[StrToEvent(evt.Event)] {
		handler(evt)
	}
}

func NewDispatcher() *dispatcher {
	return &dispatcher{events: make(map[Event][]EventHandler, 20)}
}

